package is.server.model;

import is.common.datatype.Request;
import is.common.exception.DroppingNonEmptyDepartmentException;
import is.common.exception.RowNotFoundException;
import is.common.exception.RowsAreaIncorrectException;
import is.common.exception.UniqueCheckFailedException;
import is.common.interfaces.mvc.Model;
import is.common.syntax.CommandQualifier;
import is.common.util.ObjectStreamer;
import is.common.watchdog.WatchdogServerThread;
import is.server.Conf;
import is.server.model.xtool.Xtool;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * M-part head class
 *
 * @author aspr0
 *         Date: 01.04.12
 *         Time: 5:24
 */
public class ServerModel implements Model
{
    private Xtool xtool;
    private String dbFileName;
    private ListeningThread listeningThread;
    private WatchdogServerThread watchdogServerThread;
    private int port; 
    private int controlPort; 

    private boolean activated = false;


    public void activate(String dbFileName, int port, int controlPort)
            throws IOException, SAXException, ParserConfigurationException, TransformerException
    {
        this.port = port;
        this.controlPort = controlPort;

        xtool = new Xtool(Conf.XSD_SCHEMA_FILE_NAME);

        this.dbFileName = dbFileName;
        xtool.setFileName(dbFileName);

        long dbFileLength = new File(dbFileName).length();

        if (dbFileLength == 0)
        {
            xtool.initFile();
            xtool.saveFile();
        }

        xtool.parseFile();

        /* listening cycle */
        /* once finished thread couldn't be restarted
         * restart causes IllegalThreadStateException,
         * should be recreated */
        listeningThread = new ListeningThread(this.port);
        listeningThread.start();

        /* listening cycle */
        watchdogServerThread = new WatchdogServerThread(this.controlPort);
        watchdogServerThread.start();

        activated = true;
    }

    public void deactivate() throws IOException, TransformerException
    {
        if (activated)
        {
            watchdogServerThread.deactivate();
            listeningThread.deactivate();
            xtool.saveFile();
            activated = false;
        }
    }


    public Document getDbDoc()
    {
        return xtool.getDbDoc();
    }


    @Override
    public boolean isActivated()
    {
        return activated;
    }

    /* local public */
    @Override
    public String[] getHeader(Integer sectionNum) throws IOException, ClassNotFoundException
    {
        return xtool.getHeader(sectionNum);
    }

    @Override
    public synchronized String[][] getRows(Integer sectionNum, Integer rowNumStart, Integer howMany)
            throws RowsAreaIncorrectException, RowNotFoundException, IOException, ClassNotFoundException
    {
        return xtool.getRows(sectionNum, rowNumStart, howMany);
    }

    @Override
    public synchronized void addRow(Integer sectionNum, String[] row)
            throws RowNotFoundException, UniqueCheckFailedException
    {
        xtool.addRow(sectionNum, row);
    }

    @Override
    public synchronized void editRow(Integer sectionNum, String[] row)
            throws UniqueCheckFailedException, RowNotFoundException
    {
        xtool.editRow(sectionNum, row);
    }

    @Override
    public synchronized String dropRows(Integer sectionNum, String[] rowNums) throws DroppingNonEmptyDepartmentException
    {
        return xtool.dropRows(sectionNum, rowNums);
    }

    @Override
    public synchronized String[][] findRows(Integer sectionNum, String[] masks)
            throws IOException, ClassNotFoundException, RowNotFoundException
    {
        return xtool.findRows(sectionNum, masks);
    }

    public int concat(String server, int port) throws IOException, ClassNotFoundException
    {

        Socket socket = new Socket(server, port);

        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        ObjectStreamer objectStreamer = new ObjectStreamer(objectInputStream, objectOutputStream);

        Request request = new Request(CommandQualifier.CONCAT);
        objectStreamer.transmitObject(request);

        Document dbDocAlien = objectStreamer.receiveDoc();
        
        objectOutputStream.close();
        objectInputStream.close();
        socket.close();
        
        stopListening();
        int removedDuplicatesCount = xtool.concat(dbDocAlien);
        startListening();

        return removedDuplicatesCount;
    }

    private void stopListening() throws IOException
    {
        watchdogServerThread.deactivate();
        listeningThread.deactivate();
    }

    private void startListening() throws IOException
    {
        watchdogServerThread = new WatchdogServerThread(this.controlPort);
        watchdogServerThread.start();
        listeningThread = new ListeningThread(this.port);
        listeningThread.start();
    }
}
